package com.tydic.web.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.RandomUtil;
import com.tydic.common.core.constant.Constants;
import com.tydic.common.core.constant.GlobalConstants;
import com.tydic.common.core.domain.R;
import com.tydic.common.core.exception.ServiceException;
import com.tydic.common.core.utils.SpringUtils;
import com.tydic.common.core.utils.StringUtils;
import com.tydic.common.core.utils.reflect.ReflectUtils;
import com.tydic.common.mail.config.properties.MailProperties;
import com.tydic.common.ratelimiter.annotation.RateLimiter;
import com.tydic.common.ratelimiter.enums.LimitType;
import com.tydic.common.redis.utils.RedisUtils;
import com.tydic.common.web.config.properties.CaptchaProperties;
import com.tydic.common.web.enums.CaptchaType;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;

/**
 * 验证码操作处理
 *
 * @author Lion Li
 */
@SaIgnore
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
public class CaptchaController {

    private final CaptchaProperties captchaProperties;
    private final MailProperties mailProperties;

    /**
     * 短信验证码
     *
     * @param photoNumber 用户手机号
     */
    @RateLimiter(key = "#photoNumber", time = 60, count = 1)
    @GetMapping("/common/sendSmsCode")
    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String photoNumber) {
        String key = GlobalConstants.CAPTCHA_CODE_KEY + photoNumber;
        String code = RandomUtil.randomNumbers(6);
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
        log.info("短信验证码:{}", code);
        return R.ok();
    }


    /**
     * 获取图片验证码
     */
    @RateLimiter(time = 60, count = 30, limitType = LimitType.IP)
    @GetMapping("/common/getVerificationCode")
    public void getVerificationCode(HttpServletResponse response) {
        try {
            boolean captchaEnabled = captchaProperties.getEnable();
            if (!captchaEnabled) {
                throw new ServiceException("未启用图片验证码功能");
            }
            // 生成验证码
            CaptchaType captchaType = captchaProperties.getType();
            boolean isMath = CaptchaType.MATH == captchaType;
            Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
            CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
            AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
            captcha.setGenerator(codeGenerator);
            captcha.createCode();
            String code = captcha.getCode();
            if (isMath) {
                ExpressionParser parser = new SpelExpressionParser();
                Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
                code = exp.getValue(String.class);
            }
            log.info("图片验证码:{}", code);
            String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + code.toLowerCase();
            RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
            response.setContentType("image/png");
            response.getOutputStream().write(captcha.getImageBytes());
            response.getOutputStream().flush();
        } catch (ServiceException e1) {
            throw e1;
        } catch (Exception e) {
            log.error("获取图片验证码失败", e);
            throw new ServiceException("获取图片验证码失败");
        }
    }

}
